#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <unistd.h>
#include <pthread.h>
using namespace std;
namespace ns_threadpool
{
    const int g_num = 5;
    template <class T>
    class ThreadPool
    {
    private:
        int num_;
        queue<T> task_queue_;
        pthread_mutex_t mtx_;
        pthread_cond_t cond_;
    public:
        void Lock()
        {
            pthread_mutex_lock(&mtx_);
        }

        void Unlock()
        {
            pthread_mutex_unlock(&mtx_);
        }
        bool IsEmpety()
        {
           return task_queue_.empty(); 
        }
        void Wait()
        {
            pthread_cond_wait(&cond_, &mtx_);
        }
        void WakeUp()
        {
            pthread_cond_signal(&cond_);
        }
    public:
        ThreadPool(int num = g_num):num_(num)
        {
            pthread_mutex_init(&mtx_, nullptr);  
            pthread_cond_init(&cond_, nullptr);  
        }
        
        //在类中要让线程执行类内成员方法，是不可行的
        //必须让线程执行静态方法
        static void* Rountine(void* args)
        {
            pthread_detach(pthread_self());
            ThreadPool<T>* tp = (ThreadPool<T>*)args;
            while(true)
            {
                tp->Lock();
                while(tp->IsEmpety())
                {
                    tp->Wait();
                }
                T t;
                tp->PopTask(&t);
                tp->Unlock();
                t.Run();
                
            }
        }

        void InitThreadPool()
        {
            pthread_t tid;
            for(int i = 0; i < num_; i++)
            {
                pthread_create(&tid, nullptr, Rountine, (void*)this);
            }
        }
        
        void PushTask(const T& in)
        {
            Lock();
            task_queue_.push(in);
            Unlock();
            WakeUp();
        }

        void PopTask(T* out)
        {
            *out = task_queue_.front();
            task_queue_.pop();
        }

        ~ThreadPool()
        {
            pthread_mutex_destroy(&mtx_);
            pthread_cond_destroy(&cond_);
        }
    };
}
